﻿using CodeGen.TS.Generation;
using CodeGen.TS.Input.XmlConfig;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CodeGen.TS.Input
{
    public class ConfigParser
    {
        public ConfigRoot Root { get; private set; }
        public string FileName { get; private set; }
        public string XMLConfigFile { get; set; }

        public ConfigParser(string fileName, string xmlConfigFile)
        {
            if (System.IO.Path.IsPathRooted(xmlConfigFile))
                this.XMLConfigFile = xmlConfigFile;
            else
                this.XMLConfigFile = System.IO.Path.GetFullPath(xmlConfigFile);

            this.Root = ConfigRoot.FromXMLFile(xmlConfigFile);

            if (fileName != null)
            {
                this.FileName = RootPathIfNeccessary(fileName);
            }
        }

        public string RootPathIfNeccessary(string path)
        {
            if (!System.IO.Path.IsPathRooted(path))
            {
                return System.IO.Path.Combine(System.IO.Path.GetDirectoryName(XMLConfigFile), path);
            }
            else
                return path;
        }

        public void Execute()
        {
            //Mapping: Path is the key, the types to generate are the value
            List<TypesConfig> allConfigTypes = new List<TypesConfig>();

            foreach (var config in Root.ConfigurationGroups)
            {
                foreach (var typeConfig in config.TypeConfigs)
                {
                    string dll;
                    if (!string.IsNullOrEmpty(typeConfig.SourceDll))
                    {
                        dll = RootPathIfNeccessary(typeConfig.SourceDll);
                    }
                    else if (!string.IsNullOrEmpty(config.Configuration.SourceDll))
                    {
                        dll = RootPathIfNeccessary(config.Configuration.SourceDll);
                    }
                    else if (!string.IsNullOrEmpty(FileName))
                    {
                        dll = FileName;
                    }
                    else
                    {
                        Logger.LogWarning("No dll found for {0}. No code will be generated", typeConfig.FullQualifiedName ?? typeConfig.Name);
                        continue;
                    }
                    typeConfig.SourceDll = dll;

                    string outputFile;
                    if (!string.IsNullOrEmpty(typeConfig.Output))
                    {
                        outputFile = RootPathIfNeccessary(typeConfig.Output);
                    }
                    else if (!string.IsNullOrEmpty(config.Configuration.Output))
                    {
                        outputFile = RootPathIfNeccessary(config.Configuration.Output);
                    }
                    else
                    {
                        Logger.LogWarning("No output found for {0}. No code will be generated", typeConfig.FullQualifiedName ?? typeConfig.Name);
                        continue;
                    }
                    typeConfig.Output = outputFile;
                    string modulName = typeConfig.ModulName;
                    if (string.IsNullOrEmpty(modulName))
                        modulName = config.Configuration.ModulName;
                    if (string.IsNullOrEmpty(modulName))
                        modulName = null;
                    typeConfig.ModulName = modulName;

                    if (typeConfig.RecursionLevel == int.MinValue)
                        typeConfig.RecursionLevel = config.Configuration.RecursionLevel;

                    Assembly assembly = AssemblyLoader.GetAssembly(typeConfig.SourceDll);
                    Type type;
                    if (!string.IsNullOrEmpty(typeConfig.FullQualifiedName))
                    {
                        type = assembly.GetTypeByName(typeConfig.FullQualifiedName, true);
                        if (type == null)
                        {
                            Logger.LogWarning("Type {0} not found in dll {1}", typeConfig.Name, typeConfig.SourceDll);
                            continue;
                        }
                    }
                    else if (!string.IsNullOrEmpty(typeConfig.Name))
                    {
                        type = assembly.GetTypeByName(typeConfig.Name, false);
                        if (type == null)
                        {
                            Logger.LogWarning("Type {0} not found in dll {1}", typeConfig.Name, typeConfig.SourceDll);
                            continue;
                        }
                    }
                    else
                    {
                        Logger.LogWarning("You have to specify either a fullqualified name (fqname in xml) or a name of the type. No code will be generated");
                        continue;
                    }
                    typeConfig.CLRType = type;
                    allConfigTypes.Add(typeConfig);

                }
            }


            List<ICodeGenerator> allGenerators = new List<ICodeGenerator>();

            foreach (var typeConfig in allConfigTypes)
            {
                ICodeGenerator generator;
                if (typeConfig.Generate == "class")
                {
                    generator = new Generation.Generators.ClassGenerator()
                    {
                        GeneratedWebServiceMethod = typeConfig.GeneratedWebserviceMethod,
                        UseInitialValue = typeConfig.UseInitialValue
                    };
                }
                else if (typeConfig.Generate == "interface")
                {
                    generator = new Generation.Generators.InterfaceGenerator();
                }
                else if (typeConfig.Generate == "apicontroller")
                {
                    generator = new Generation.Generators.MVC.ApiController();
                }
                else
                {
                    Logger.LogWarning("generate attribute \"{0}\" is unknown. No Code will be generated", typeConfig.Generate);
                    continue;
                }
                generator.RecursionLevel = typeConfig.RecursionLevel;
                generator.NewTypeName = typeConfig.NewName;
                generator.Type = typeConfig.CLRType;
                generator.UseObservable = typeConfig.UseObservable;
                generator.ModulName = typeConfig.ModulName;
                generator.OutputFileName = typeConfig.Output;
                Output.TypeGeneratorFactory.RegisterGenerator(generator);


            }
            Output.TypeGeneratorFactory.WriteFiles();

        }


    }
}
